﻿using System.Configuration;
using System.Diagnostics;

namespace MockingBird.Simulator.Interceptors.Classic
{
    using System;
    using System.Text;
    using System.Web;
    using System.Xml;
    using Common.Infrastructure.DependencyResolution;
    using Common.Infrastructure.Http;
    using Common.Infrastructure.Logging;
    using Microsoft.Practices.Unity;
    using MockingBird.Simulator.Framework.Configuration;
    using Simulator.Framework;
    using Simulator.Framework.Exceptions;
    using Simulator.Framework.Interfaces;

    public class ClassicInterceptor : IHttpHandler
    {
        public IExecutionContext ExecContext { get; set; }
        public IRequestDistributor Distributor { get; set; }
        public ILogger Logger { get; set; }
        public ISoapFaultBuilder FaultBuilder { get; set; }
        private IConfigurationDataProvider configProvider;
        private ApplicationConfiguration simulationConfig;

        #region IHttpHandler Members

        /// <summary>
        /// This will return true because there is no state held in the handler so there is no reason 
        /// why another request cannot pass through this same instance
        /// </summary>
        public bool IsReusable
        {
            get { return true; }
        }

        /// <summary>
        /// Method called by the .NET Framework with the current httpContext
        /// </summary>
        /// <param name="httpContext">HttpContext set by the framework</param>
        public void ProcessRequest(HttpContext httpContext)
        {
            System.Diagnostics.Trace.WriteLine("Classic Interceptor.Process Request");
            if (httpContext == null)
            {
                throw new ArgumentNullException("httpContext");
            }
            try
            {
                this.InitDependencies();
                this.ProcessRequestInternal(httpContext);
            }
            catch (Exception e)
            {
                this.WriteHttpResponse(e.ToString());
            }

        }

        /// <summary>
        /// Core method that picks up the request, signals its arrival and then hands it off for processing
        /// Uses the httpContext property
        /// </summary>
        public void ProcessRequestInternal(HttpContext context)
        {
            if (Logger == null)
            {
                this.ResolveLogger();
            }

            //Exit if the context is null
            this.ValidateContext();

            //get the uri from the incoming request
            string uri = ExecContext.Uri.AbsoluteUri;

            //Call the main method of the logger
            Logger.LogRequestReceived(uri);

            //Now call the appropriate handler or call the distributor
            if (Distributor == null)
            {
                this.ResolveDistributor();
            }

            try
            {
                XmlDocument response = Distributor.ProcessRequest(this.ExecContext);
                this.ExecContext.ResponseMessage = response;

                //Now inject the message into the http response stream
                this.ConstructHttpResponse(context);

            }
            catch (ApplicationOperationException aex)
            {
                LogException(aex);
                //Now we construct a SoapFault and send back a proper response
                SendSoapFaultToClient(aex);
            }
            //temporarily catch a generic exception
            catch (Exception e)
            {
                LogException(e);
                SendSoapFaultToClient(e);
            }
        }

        #endregion

        #region "private methods"

        private void ResolveDistributor()
        {
            DependencyResolver.ConfigureDefaultContainer("PerRequest");
            IHandlerDispenser dispenser = DependencyResolver.DefaultContainer.Resolve<IHandlerDispenser>();
            this.Distributor = new MockingBird.Simulator.Framework.RequestDistributor(this.ExecContext, this.Logger, dispenser);
            // Distributor = DependencyResolver.DefaultContainer.Resolve<IRequestDistributor>();
        }

        private void ValidateContext()
        {
            if (this.ExecContext == null)
            {
                var msg = "ExecutionContext Is Null";
                var ex = new InvalidOperationException(msg);
                this.Logger.LogError(msg, ex);
                throw ex;
            }
        }


        private void InitDependencies()
        {
            Trace.WriteLine("Initializing dependencies");
            DependencyResolver.ConfigurationFile = ConfigurationManager.AppSettings["mockingbird_configuration_folder"];
            Trace.WriteLine("Unity config file path : " + DependencyResolver.ConfigurationFile);

            Trace.WriteLine("Configuring default container");
            DependencyResolver.ConfigureDefaultContainer();

            this.configProvider = DependencyResolver.DefaultContainer.Resolve<IConfigurationDataProvider>();
            this.simulationConfig = new ApplicationConfiguration(this.configProvider);
            this.ExecContext = new ExecutionContext(this.simulationConfig, HttpContext.Current);
            DependencyResolver.DefaultContainer.RegisterInstance<IExecutionContext>(this.ExecContext, new PerThreadLifetimeManager());
        }

        private void ResolveLogger()
        {
            DependencyResolver.ConfigureDefaultContainer();
            LoggingConfigurationProvider lcp = new LoggingConfigurationProvider();
            lcp.LoggingConfigurationFolder = this.simulationConfig.ConfigurationFilesRoot;
            DependencyResolver.DefaultContainer.RegisterInstance<ILoggingConfigurationProvider>(lcp);

            this.Logger = DependencyResolver.DefaultContainer.Resolve<ILogger>();
        }

        private void ResolveSoapFaultBuilder()
        {

            DependencyResolver.ConfigureDefaultContainer("PerRequest");
            LoggingConfigurationProvider lcp = new LoggingConfigurationProvider();
            lcp.LoggingConfigurationFolder = this.simulationConfig.ConfigurationFilesRoot;
            DependencyResolver.DefaultContainer.RegisterInstance<ILoggingConfigurationProvider>(lcp);

            FaultBuilder = DependencyResolver.DefaultContainer.Resolve<ISoapFaultBuilder>();

        }

        private void ConstructHttpResponse(HttpContext context)
        {
            // If this is a one way method then there is no need to construct a response.
            if (this.ExecContext.IsOneWay)
            {
                return;
            }
            HttpContext httpContext = HttpContext.Current;
            httpContext.Response.ContentType = "text/xml";
            XmlTextWriter xtw = new XmlTextWriter(httpContext.Response.OutputStream, Encoding.UTF8);
            this.ExecContext.ResponseMessage.WriteTo(xtw);
            xtw.Close();
        }

        private void LogException(Exception e)
        {
            Logger.LogFatal("Classic Interceptor: An exception occurred in your application", e);
        }

        private void SendSoapFaultToClient(Exception e)
        {
            if (this.FaultBuilder == null)
            {
                this.ResolveSoapFaultBuilder();
            }
            XmlDocument faultDoc = FaultBuilder.BuildSoapFault(e);
            ExecContext.WriteResponse(faultDoc);
            this.ConstructHttpResponse(HttpContext.Current);
        }

        private void WriteHttpResponse(string data)
        {
            HttpResponseWriter.WriteResponse(data);
        }

        #endregion
    }
}
